namespace ScrumSprintMonitor.Modules.WorkItemMonitor.ViewModels
{
	#region #using Directives

	using System;
	using System.Deployment.Application;
	using System.Diagnostics;
	using System.Reflection;
	using System.Windows;
	using System.Windows.Controls;
	using System.Windows.Input;
	using System.Windows.Threading;

	using Extensibility.Events;

	using Infrastructure;
	using Infrastructure.Configuration;

	using Microsoft.Practices.Composite.Events;
	using Microsoft.Practices.Composite.Presentation.Commands;
	using Microsoft.Practices.ServiceLocation;

	using UI.Wpf.ViewModels;

	#endregion

	public class MonitorViewModel : SprintViewModelBase,
	                                IMonitorViewModel
	{
		static MonitorViewModel()
		{
			var dependsOn = Register<MonitorViewModel>();

			dependsOn.Call(me => me.UpdateInitializedFlag()).OnChanged(me => me.ReportableData.CurrentSprint.LastUpdate).OnChanged(
				me => me.ReportableData.CurrentSprint.Status);
			dependsOn.Call(me => me.Exception = (me.LastBuild != null ? me.LastBuild.Exception : null)).OnChanged(me => me.ReportableData.LastBuild.Exception);
			dependsOn.Call(me => me.Exception = (me.CurrentSprint != null ? me.CurrentSprint.Exception : null)).OnChanged(
				me => me.ReportableData.CurrentSprint.Exception);
			dependsOn.Call(me => me.OnPropertyChanged("SprintStatus")).OnChanged(me => me.ReportableData.CurrentSprint.Status);
			dependsOn.Call(me => me.OnPropertyChanged("LastRefresh")).OnChanged(me => me.ReportableData.CurrentSprint.LastRefresh);
			dependsOn.Call(me => me.OnPropertyChanged("LastUpdate")).OnChanged(me => me.ReportableData.CurrentSprint.LastUpdate);
			dependsOn.Call(me => me.refreshCommand.RaiseCanExecuteChanged()).OnChanged(me => me.ReportableData.Connection.IsConnected);

			dependsOn.Call(me => me.OnPropertyChanged("ExceptionMessage")).OnChanged(me => me.Exception);
			dependsOn.Call(me => me.UpdateActiveView()).OnChanged(me => me.Exception).OnChanged(me => me.IsInitialized).OnChanged(me => me.SprintStatus);
			dependsOn.Call(me => me.UpdateShowProgressFlag()).OnChanged(me => me.ActiveView).OnChanged(me => me.SprintStatus);
		}

		public MonitorViewModel(IServiceLocator serviceLocator, IEventAggregator eventAggregator, IShellLayoutConfiguration shellLayoutConfiguration)
			: base(serviceLocator, false)
		{
			this.shellLayoutConfiguration = shellLayoutConfiguration;
			this.eventAggregator = eventAggregator;
			this.refreshCommand = new DelegateCommand<object>(OnRefresh, CanRefresh);
			this.invokeConfigurationCommand = new DelegateCommand<object>(OnInvokeConfiguration);

			if (Application.Current != null)
			{
				this.primaryWindow = Application.Current.Windows[0];
				if (this.primaryWindow != null)
				{
					this.primaryWindow.SizeChanged += primaryWindow_SizeChanged;
				}
			}

			UpdateSprintTitleOrientation();
			UpdateActiveView();
		}

		#region Private Fields

		private readonly IEventAggregator eventAggregator;
		private readonly ICommand invokeConfigurationCommand;
		private readonly Window primaryWindow;
		private readonly DelegateCommand<object> refreshCommand;
		private readonly IShellLayoutConfiguration shellLayoutConfiguration;

		private MonitorViewType activeView;
		private bool isInitialized;
		private bool showProgress;
		private Orientation sprintTitleOrientation;

		#endregion

		#region Private Methods

		private IBuildInfo LastBuild
		{
			get
			{
				EnsureConnected();
				return (ReportableData != null ? ReportableData.LastBuild : null);
			}
		}

		private bool IsInitialized
		{
			get { return this.isInitialized; }
			set
			{
				if (value != this.isInitialized)
				{
					this.isInitialized = value;

					OnPropertyChanged("IsInitialized");
				}
			}
		}

		private void UpdateInitializedFlag()
		{
			if (!IsInitialized && CurrentSprint.Status == SprintStatus.OK)
			{
				PostAction(DispatcherPriority.ApplicationIdle, () => IsInitialized = true);
			}
		}

		private void UpdateActiveView()
		{
			if (Exception != null)
			{
				ActiveView = (Exception is RemainingWorkNotEstimatedException ? MonitorViewType.FoundUnassignedWorkItems : MonitorViewType.ExceptionView);
			}
			else
			{
				ActiveView = (IsInitialized ? MonitorViewType.SprintView : MonitorViewType.None);
			}
		}

		private void UpdateShowProgressFlag()
		{
			switch (ActiveView)
			{
				case MonitorViewType.None:
				case MonitorViewType.SprintView:
					ShowProgress = (SprintStatus == SprintStatus.NoDataAvailable);
					break;
				default:
					ShowProgress = false;
					break;
			}
		}

		private void UpdateSprintTitleOrientation()
		{
			if (this.primaryWindow != null)
			{
				double minWidthToHeightRatio = (this.shellLayoutConfiguration.MultiMonitor ? 4.0 / 3.0 : 16.0 / 11.0);

				if (this.primaryWindow.ActualWidth / this.primaryWindow.ActualHeight > minWidthToHeightRatio)
				{
					SprintTitleOrientation = Orientation.Vertical;
					return;
				}
			}

			SprintTitleOrientation = Orientation.Horizontal;
		}

		private bool CanRefresh(object arg)
		{
			return ReportableData != null && ReportableData.Connection.IsConnected;
		}

		private void OnRefresh(object obj)
		{
			if (CanRefresh(obj))
			{
				CurrentSprint.Refresh(true);
			}
		}

		private void OnInvokeConfiguration(object obj)
		{
			this.eventAggregator.GetEvent<InvokeConfigurationUiEvent>().Publish(null);
		}

		#endregion

		#region IMonitorViewModel Members

		public string DeploymentVersion
		{
			get
			{
				GuardDisposed();

				//Default to the version of the currently executing Assembly
				string version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).FileVersion;

				//Check to see if we are ClickOnce Deployed.
				//i.e. the executing code was installed via ClickOnce
				if (ApplicationDeployment.IsNetworkDeployed)
				{
					//Collect the ClickOnce Current Version
					version = ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString();
				}

				return version;
			}
		}

		public new Exception Exception
		{
			get { return base.Exception; }
			private set { base.Exception = value; }
		}

		public string ExceptionMessage
		{
			get
			{
				if (Exception != null)
				{
					var selectedException = Exception;

					while (selectedException.InnerException != null && (selectedException is ActivationException || selectedException is TargetInvocationException))
					{
						selectedException = selectedException.InnerException;
					}

					return selectedException.Message;
				}

				return string.Empty;
			}
		}

		public MonitorViewType ActiveView
		{
			get { return this.activeView; }
			private set
			{
				if (value != this.activeView)
				{
					this.activeView = value;

					OnPropertyChanged("ActiveView");
				}
			}
		}

		public bool ShowProgress
		{
			get { return this.showProgress; }
			set
			{
				if (value != this.showProgress)
				{
					this.showProgress = value;

					OnPropertyChanged("ShowProgress");
				}
			}
		}

		public Orientation SprintTitleOrientation
		{
			get { return this.sprintTitleOrientation; }
			private set
			{
				if (value != this.sprintTitleOrientation)
				{
					this.sprintTitleOrientation = value;

					OnPropertyChanged("SprintTitleOrientation");
				}
			}
		}

		public SprintStatus SprintStatus
		{
			get
			{
				EnsureConnected(false);
				return (CurrentSprint != null ? CurrentSprint.Status : SprintStatus.NoDataAvailable);
			}
		}

		public SprintMode SprintMode
		{
			get { return this.shellLayoutConfiguration.ActiveSprintMode; }
		}

		public DateTime LastRefresh
		{
			get { return (CurrentSprint != null ? CurrentSprint.LastRefresh.GetValueOrDefault(DateTime.MinValue) : SystemTime.Now()); }
		}

		public DateTime LastUpdate
		{
			get { return (CurrentSprint != null ? CurrentSprint.LastUpdate.GetValueOrDefault(DateTime.MinValue) : SystemTime.Now()); }
		}

		public ICommand RefreshCommand
		{
			get { return this.refreshCommand; }
		}

		public ICommand InvokeConfigurationCommand
		{
			get { return this.invokeConfigurationCommand; }
		}

		#endregion

		#region Event Handlers

		private void primaryWindow_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			UpdateSprintTitleOrientation();
		}

		#endregion
	}
}